package cn.com.czpb.client.custorm;

import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.FrameLayout;

import cn.com.czpb.client.utils.TUtils;


/**
 * This class serves as a WebChromeClient to be set to a WebView, allowing it to
 * play video. Video will play differently depending on target API level
 * (in-line, fullscreen, or both).
 *
 * It has been tested with the following video classes: -
 * android.widget.VideoView (typically API level <11) -
 * android.webkit.HTML5VideoFullScreen$VideoSurfaceView/VideoTextureView
 * (typically API level 11-18) -
 * com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView
 * (typically API level 19+)
 * 
 * Important notes: - For API level 11+, android:hardwareAccelerated="true" must
 * be set in the application manifest. - The invoking activity must call
 * VideoEnabledWebChromeClient's onBackPressed() inside of its own
 * onBackPressed(). - Tested in Android API levels 8-19. Only tested on
 * http://m.youtube.com.
 *
 * @author Cristian Perez (http://cpr.name)
 *
 */
public class VideoEnabledWebChromeClient extends WebChromeClient implements
        OnPreparedListener, OnCompletionListener, OnErrorListener {
	public interface ToggledFullscreenCallback {
		void toggledFullscreen(boolean fullscreen);
	}

	private View activityNonVideoView;
	private ViewGroup activityVideoView;
	private View loadingView;
	private VideoEnabledWebView webView;

	private boolean isVideoFullscreen; // Indicates if the video is being
										// displayed using a custom view
										// (typically full-screen)
	private FrameLayout videoViewContainer;
	private CustomViewCallback videoViewCallback;

	private ToggledFullscreenCallback toggledFullscreenCallback;
	

	/**
	 * Never use this constructor alone. This constructor allows this class to
	 * be defined as an inline inner class in which the user can override
	 * methods
	 */
	@SuppressWarnings("unused")
	public VideoEnabledWebChromeClient() {
	}

	/**
	 * Builds a video enabled WebChromeClient.
	 * 
	 * @param activityNonVideoView
	 *            A View in the activity's layout that contains every other view
	 *            that should be hidden when the video goes full-screen.
	 * @param activityVideoView
	 *            A ViewGroup in the activity's layout that will display the
	 *            video. Typically you would like this to fill the whole layout.
	 */
	@SuppressWarnings("unused")
	public VideoEnabledWebChromeClient(View activityNonVideoView,
                                       ViewGroup activityVideoView) {
		this.activityNonVideoView = activityNonVideoView;
		this.activityVideoView = activityVideoView;
		this.loadingView = null;
		this.webView = null;
		this.isVideoFullscreen = false;
	}

	/**
	 * Builds a video enabled WebChromeClient.
	 * 
	 * @param activityNonVideoView
	 *            A View in the activity's layout that contains every other view
	 *            that should be hidden when the video goes full-screen.
	 * @param activityVideoView
	 *            A ViewGroup in the activity's layout that will display the
	 *            video. Typically you would like this to fill the whole layout.
	 * @param loadingView
	 *            A View to be shown while the video is loading (typically only
	 *            used in API level <11). Must be already inflated and without a
	 *            parent view.
	 */
	@SuppressWarnings("unused")
	public VideoEnabledWebChromeClient(View activityNonVideoView,
                                       ViewGroup activityVideoView, View loadingView) {
		this.activityNonVideoView = activityNonVideoView;
		this.activityVideoView = activityVideoView;
		this.loadingView = loadingView;
		this.webView = null;
		this.isVideoFullscreen = false;
	}

	/**
	 * Builds a video enabled WebChromeClient.
	 * 
	 * @param activityNonVideoView
	 *            A View in the activity's layout that contains every other view
	 *            that should be hidden when the video goes full-screen.
	 * @param activityVideoView
	 *            A ViewGroup in the activity's layout that will display the
	 *            video. Typically you would like this to fill the whole layout.
	 * @param loadingView
	 *            A View to be shown while the video is loading (typically only
	 *            used in API level <11). Must be already inflated and without a
	 *            parent view.
	 * @param webView
	 *            The owner VideoEnabledWebView. Passing it will enable the
	 *            VideoEnabledWebChromeClient to detect the HTML5 video ended
	 *            event and exit full-screen. Note: The web page must only
	 *            contain one video tag in order for the HTML5 video ended event
	 *            to work. This could be improved if needed (see Javascript
	 *            code).
	 */
	public VideoEnabledWebChromeClient(View activityNonVideoView,
                                       ViewGroup activityVideoView, View loadingView,
                                       VideoEnabledWebView webView) {
		this.activityNonVideoView = activityNonVideoView;
		this.activityVideoView = activityVideoView;
		this.loadingView = loadingView;
		this.webView = webView;
		this.isVideoFullscreen = false;
	}

	/**
	 * Indicates if the video is being displayed using a custom view (typically
	 * full-screen)
	 * 
	 * @return true it the video is being displayed using a custom view
	 *         (typically full-screen)
	 */
	public boolean isVideoFullscreen() {
		return isVideoFullscreen;
	}

	/**
	 * Set a callback that will be fired when the video starts or finishes
	 * displaying using a custom view (typically full-screen)
	 * 
	 * @param callback
	 *            A VideoEnabledWebChromeClient.ToggledFullscreenCallback
	 *            callback
	 */
	public void setOnToggledFullscreen(ToggledFullscreenCallback callback) {
		this.toggledFullscreenCallback = callback;
	}

	@Override
	public void onShowCustomView(View view, CustomViewCallback callback) {
		if (view instanceof FrameLayout) {
			// A video wants to be shown
			FrameLayout frameLayout = (FrameLayout) view;
			View focusedChild = frameLayout.getFocusedChild();

			// Save video related variables
			this.isVideoFullscreen = true;
			this.videoViewContainer = frameLayout;
			this.videoViewCallback = callback;

			// Hide the non-video view, add the video view, and show it
			activityNonVideoView.setVisibility(View.INVISIBLE);
			activityVideoView.addView(videoViewContainer, new LayoutParams(
					LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
			activityVideoView.setVisibility(View.VISIBLE);

			if (focusedChild instanceof android.widget.VideoView) {
				// android.widget.VideoView (typically API level <11)
				android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;

				// Handle all the required events
				videoView.setOnPreparedListener(this);
				videoView.setOnCompletionListener(this);
				videoView.setOnErrorListener(this);
			} else {
				// Other classes, including:
				// - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which
				// inherits from android.view.SurfaceView (typically API level
				// 11-18)
				// - android.webkit.HTML5VideoFullScreen$VideoTextureView, which
				// inherits from android.view.TextureView (typically API level
				// 11-18)
				// -
				// com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView,
				// which inherits from android.view.SurfaceView (typically API
				// level 19+)

				// Handle HTML5 video ended event only if the class is a
				// SurfaceView
				// Test case: TextureView of Sony Xperia T API level 16 doesn't
				// work fullscreen when loading the javascript below
				if (webView != null
						&& webView.getSettings().getJavaScriptEnabled()
						&& focusedChild instanceof SurfaceView) {
					// Run javascript code that detects the video end and
					// notifies the Javascript interface
					String js = "javascript:";
					js += "var _ytrp_html5_video_last;";
					js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
					js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {";
					{
						js += "_ytrp_html5_video_last = _ytrp_html5_video;";
						js += "function _ytrp_html5_video_ended() {";
						{
							js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must
																			// match
																			// Javascript
																			// interface
																			// name
																			// and
																			// method
																			// of
																			// VideoEnableWebView
						}
						js += "}";
						js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
					}
					js += "}";
					webView.loadUrl(js);
				}
			}

			// Notify full-screen change
			if (toggledFullscreenCallback != null) {
				toggledFullscreenCallback.toggledFullscreen(true);
			}
		}
	}

	@Override
	@SuppressWarnings("deprecation")
	public void onShowCustomView(View view, int requestedOrientation,
                                 CustomViewCallback callback){ // Available in API level 14+,
											// deprecated in API level 18+
		Log.i("", "onShowCustomView");
		onShowCustomView(view, callback);
	}

	@Override
	public void onHideCustomView() {
		// This method should be manually called on video end in all cases
		// because it's not always called automatically.
		// This method must be manually called on back key press (from this
		// class' onBackPressed() method).

		if (isVideoFullscreen) {
			// Hide the video view, remove it, and show the non-video view
			activityVideoView.setVisibility(View.INVISIBLE);
			activityVideoView.removeView(videoViewContainer);
			activityNonVideoView.setVisibility(View.VISIBLE);

			// Call back (only in API level <19, because in API level 19+ with
			// chromium webview it crashes)
			if (videoViewCallback != null
					&& !videoViewCallback.getClass().getName()
							.contains(".chromium.")) {
				videoViewCallback.onCustomViewHidden();
			}

			// Reset video related variables
			isVideoFullscreen = false;
			videoViewContainer = null;
			videoViewCallback = null;

			// Notify full-screen change
			if (toggledFullscreenCallback != null) {
				toggledFullscreenCallback.toggledFullscreen(false);
			}
		}
	}

	@Override
	public View getVideoLoadingProgressView() // Video will start loading
	{
		if (loadingView != null) {
			loadingView.setVisibility(View.VISIBLE);
			return loadingView;
		} else {
			return super.getVideoLoadingProgressView();
		}
	}

	@Override
	public void onPrepared(MediaPlayer mp) // Video will start playing, only
											// called in the case of
											// android.widget.VideoView
											// (typically API level <11)
	{
		if (loadingView != null) {
			loadingView.setVisibility(View.GONE);
		}
	}

	@Override
	public void onCompletion(MediaPlayer mp) // Video finished playing, only
												// called in the case of
												// android.widget.VideoView
												// (typically API level <11)
	{
		onHideCustomView();
	}

	@Override
	public boolean onError(MediaPlayer mp, int what, int extra) // Error while
																// playing
																// video, only
																// called in the
																// case of
																// android.widget.VideoView
																// (typically
																// API level
																// <11)
	{
		return false; // By returning false, onCompletion() will be called
	}

	/**
	 * Notifies the class that the back key has been pressed by the user. This
	 * must be called from the Activity's onBackPressed(), and if it returns
	 * false, the activity itself should handle it. Otherwise don't do anything.
	 * 
	 * @return Returns true if the event was handled, and false if was not
	 *         (video view is not visible)
	 */
	public boolean onBackPressed() {
		if (isVideoFullscreen) {
			onHideCustomView();
			return true;
		} else {
			return false;
		}
	}

	@Override
	public boolean onJsAlert(WebView view, String url, String message,
                             JsResult result) {
		TUtils.toast(message);
		result.cancel();
		return true;
	}

}
